Flutter Mobile, Tablet & Desktop UI
Flutter allows developers to build applications from a single codebase that can run across mobile, tablet, desktop, and web environments. However, the same UI should not always be displayed in exactly the same way on every screen. A good Flutter application adapts its layout, navigation, spacing, content density, and interaction patterns according to the space available to it.
Flutter's official adaptive-design guidance explains that responsive design focuses on fitting UI into available space, while adaptive design focuses on making the UI usable in that space. In practice, mobile, tablet, and desktop applications commonly use both approaches together. Flutter Adaptive and Responsive Design Documentation
1. Understanding Mobile, Tablet and Desktop UI
A Flutter application may run in very different environments:
- Mobile: Small touch-oriented screens with limited horizontal space.
- Tablet: Larger touch screens that can support multi-column layouts.
- Desktop: Large windows that usually have mouse, keyboard, trackpad, and larger display space.
- Web: Browser windows that can be resized to many different widths.
The goal is not simply to create three completely separate applications. Instead, share common data, business logic, and reusable widgets while changing the presentation where necessary.
2. Why Mobile, Tablet and Desktop UI Need Different Layouts
A mobile screen has limited width, so content is generally arranged vertically. A tablet can display more content side-by-side. A desktop window may provide enough space for a sidebar, navigation rail, content area, tables, filters, and additional actions.
Mobile:
+----------------------+
| App Bar |
+----------------------+
| |
| Content |
| |
| |
+----------------------+
| Home | Search | Me |
+----------------------+
Tablet:
+--------------------------------+
| App Bar |
+--------------------------------+
| Navigation | Main Content |
| | |
| | |
+--------------------------------+
Desktop:
+------------------------------------------------+
| Sidebar | Main Content | Details |
| | | |
| | | |
| | | |
+------------------------------------------------+
3. Mobile UI Design
Mobile interfaces generally have limited horizontal space and are primarily touch-driven. Therefore, mobile UI should prioritize simple navigation, readable content, large touch targets, and vertical scrolling.
Common Mobile UI Components
AppBar
NavigationBar
Drawer
ListView
GridView
BottomSheet
FloatingActionButton
SafeArea
SingleChildScrollView
Example Mobile Scaffold
Scaffold(
appBar: AppBar(
title: const Text('My App'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
),
ListTile(
leading: Icon(Icons.person),
title: Text('Profile'),
),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: 0,
destinations: [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
)
NavigationBar is a Material 3 component designed for persistent navigation between primary destinations. Flutter NavigationBar API
4. Tablet UI Design
Tablets provide significantly more horizontal space than phones. Instead of simply enlarging the mobile layout, developers can use the additional space to show more information simultaneously.
Common Tablet Patterns
- Two-column layouts.
- Navigation rail.
- List and detail views side-by-side.
- Two or three-column grids.
- Wider forms.
- More visible actions.
- Expanded dashboards.
Example Tablet Layout
Row(
children: [
SizedBox(
width: 250,
child: ListView(
children: const [
ListTile(title: Text('Dashboard')),
ListTile(title: Text('Products')),
ListTile(title: Text('Orders')),
],
),
),
const VerticalDivider(width: 1),
const Expanded(
child: Center(
child: Text('Main Content'),
),
),
],
)
5. Desktop UI Design
Desktop applications have large horizontal and vertical space and commonly support mouse, keyboard, trackpad, hover interactions, keyboard shortcuts, context menus, and resizable windows.
Common Desktop UI Components
- Sidebar navigation.
- Navigation rail.
- Navigation drawer.
- Data tables.
- Multi-column dashboards.
- Toolbars.
- Search fields.
- Context menus.
- Hover interactions.
- Keyboard shortcuts.
- Resizable content areas.
Example Desktop Layout
Row(
children: [
SizedBox(
width: 240,
child: Sidebar(),
),
const VerticalDivider(width: 1),
Expanded(
child: MainContent(),
),
],
)
Flutter's adaptive guidance recommends designing for the strengths of each form factor rather than simply making every platform look identical. Flutter Adaptive Design Best Practices
6. Mobile vs Tablet vs Desktop
| Feature | Mobile | Tablet | Desktop |
|---|
| Screen Space | Limited | Medium/Large | Large |
| Primary Input | Touch | Touch + Keyboard | Mouse + Keyboard + Trackpad |
| Navigation | NavigationBar/Drawer | NavigationRail/NavigationBar | Sidebar/Rail |
| Content | Mostly single-column | Multi-column | Multi-column |
| Forms | Stacked | Partially horizontal | Multi-column where appropriate |
| Tables | Often replaced by cards/lists | Condensed tables | Full tables |
| Dialogs | Compact | Medium | Constrained desktop dialog |
| Interaction | Touch-first | Touch + pointer | Pointer + keyboard |
7. Responsive and Adaptive Design
Responsive design changes the placement and sizing of UI elements so they fit the available space. Adaptive design can change the actual structure of the interface.
Responsive Example
Row(
children: [
Expanded(child: Card()),
Expanded(child: Card()),
],
)
Adaptive Example
if (isSmallScreen) {
return const MobileLayout();
}
return const DesktopLayout();
Flutter recommends measuring the current available window or widget constraints instead of assuming that a physical device type always corresponds to a particular amount of application space. Flutter General Approach to Adaptive Apps
8. Choosing Breakpoints
Breakpoints determine when your UI changes its structure. A commonly used example is:
const mobileBreakpoint = 600.0;
const tabletBreakpoint = 900.0;
Example:
- Below 600: Mobile-style layout.
- 600 to below 900: Tablet-style layout.
- 900 and above: Desktop-style layout.
These numbers are examples rather than universal device classifications. Flutter's adaptive documentation uses 600 logical pixels as a common example for switching between small and larger layouts, but breakpoints should ultimately be chosen according to the needs of the UI. Flutter Adaptive Layout Tutorial
9. Using MediaQuery
MediaQuery.sizeOf(context) is useful when you need to know the size of the application's current window.
final size = MediaQuery.sizeOf(context);
if (size.width < 600) {
return const MobileLayout();
}
return const LargeLayout();
Complete Example
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
if (width < 600) {
return const MobileLayout();
}
if (width < 900) {
return const TabletLayout();
}
return const DesktopLayout();
}
}
10. Using LayoutBuilder
LayoutBuilder is useful when the decision should be based on the space available to a particular widget rather than the entire application window.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileLayout();
}
if (constraints.maxWidth < 900) {
return const TabletLayout();
}
return const DesktopLayout();
},
)
The builder receives a BoxConstraints object containing the minimum and maximum width and height available from the parent. MediaQuery and LayoutBuilder Guidance
11. MediaQuery vs LayoutBuilder
| MediaQuery | LayoutBuilder |
|---|
| Measures the application window. | Measures the constraints provided by the parent. |
| Useful for full-screen adaptive decisions. | Useful for local widget adaptation. |
| Returns window size information. | Provides BoxConstraints. |
| Good for application navigation. | Good for adaptive cards/components. |
12. Adaptive Navigation
Navigation is one of the most visible differences between mobile, tablet, and desktop UI.
Mobile
NavigationBar(
selectedIndex: 0,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
)
Tablet/Desktop
NavigationRail(
selectedIndex: 0,
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.settings),
label: Text('Settings'),
),
],
)
NavigationRail is intended for wider layouts such as desktop or tablet landscape layouts, while a bottom navigation pattern is generally better suited to smaller layouts. Flutter NavigationRail API
13. Adaptive Navigation Example
class AdaptiveNavigation extends StatelessWidget {
const AdaptiveNavigation({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLarge = constraints.maxWidth >= 600;
if (isLarge) {
return Scaffold(
body: Row(
children: [
NavigationRail(
selectedIndex: 0,
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Profile'),
),
],
),
const Expanded(
child: MainContent(),
),
],
),
);
}
return Scaffold(
body: const MainContent(),
bottomNavigationBar: NavigationBar(
selectedIndex: 0,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
},
);
}
}
14. Mobile List UI
Mobile applications commonly display information as vertically scrolling lists.
ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
leading: const Icon(Icons.shopping_bag),
title: Text(product.name),
subtitle: Text(product.price),
trailing: const Icon(Icons.chevron_right),
),
);
},
)
15. Tablet Product Grid
On larger screens, a list can often become a grid so that the additional horizontal space is used effectively.
GridView.builder(
padding: const EdgeInsets.all(24),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 280,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 0.8,
),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductCard(
product: products[index],
);
},
)
Flutter's large-screen guidance recommends grid-based approaches for avoiding overly wide content and suggests basing grid sizing on available window space rather than hard-coding a device-specific number of columns. Flutter Large Screen Guidance
16. Adaptive Product Grid
LayoutBuilder(
builder: (context, constraints) {
return GridView.builder(
padding: const EdgeInsets.all(20),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: constraints.maxWidth < 600
? 500
: 280,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 0.8,
),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductCard(
product: products[index],
);
},
);
},
)
17. Mobile Form UI
Forms on mobile are generally arranged vertically because horizontal space is limited.
Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
],
)
18. Tablet/Desktop Form UI
On wider screens, related fields can be placed side-by-side.
Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
),
],
)
Adaptive Form
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
if (isWide) {
return Row(
children: [
Expanded(child: FirstNameField()),
const SizedBox(width: 16),
Expanded(child: LastNameField()),
],
);
}
return Column(
children: [
FirstNameField(),
const SizedBox(height: 16),
LastNameField(),
],
);
},
)
19. Adaptive Login UI
class AdaptiveLogin extends StatelessWidget {
const AdaptiveLogin({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 450,
),
child: Column(
children: [
const FlutterLogo(size: 90),
const SizedBox(height: 24),
const Text(
'Welcome Back',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 24),
TextField(
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: const Text('Login'),
),
),
],
),
),
),
),
),
);
}
}
20. List-Detail Layout
A list-detail layout is especially useful on tablets and desktops.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 700) {
return Row(
children: [
SizedBox(
width: 300,
child: ContactList(),
),
const VerticalDivider(width: 1),
const Expanded(
child: ContactDetails(),
),
],
);
}
return ContactList();
},
)
Flutter's adaptive tutorial uses this type of layout: large screens can show groups and details side-by-side, while smaller screens can navigate between them. Flutter List-Detail Adaptive Layout Tutorial
21. Desktop Sidebar
Row(
children: [
SizedBox(
width: 250,
child: Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text(
'My App',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
ListTile(
leading: const Icon(Icons.dashboard),
title: const Text('Dashboard'),
onTap: () {},
),
ListTile(
leading: const Icon(Icons.people),
title: const Text('Users'),
onTap: () {},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Settings'),
onTap: () {},
),
],
),
),
),
const VerticalDivider(width: 1),
const Expanded(
child: MainContent(),
),
],
)
22. Avoid Excessive Width on Desktop
Large monitors provide a lot of horizontal space, but that does not mean every widget should stretch across the entire window.
Use a maximum width for forms, articles, dialogs, and other content where excessive width would reduce readability.
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 1000,
),
child: Padding(
padding: const EdgeInsets.all(32),
child: MainContent(),
),
),
)
23. Using Expanded and Flexible
Expanded
Row(
children: [
SizedBox(
width: 250,
child: Sidebar(),
),
Expanded(
child: MainContent(),
),
],
)
Flexible
Row(
children: [
Flexible(
child: Text(
'A long piece of text that can shrink when space is limited.',
),
),
const Icon(Icons.info),
],
)
These widgets help prevent unnecessary fixed dimensions and allow layouts to use available space more effectively.
24. SafeArea
SafeArea helps keep important content away from areas affected by system UI and display cutouts.
Scaffold(
body: SafeArea(
child: YourResponsiveContent(),
),
)
This is especially useful on mobile devices where status bars, navigation areas, display cutouts, and rounded corners can affect usable space.
25. Supporting Orientation Changes
Applications should be prepared for changes in available width and height. A mobile application may change from portrait to landscape, and desktop or tablet applications may be resized continuously.
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth > constraints.maxHeight;
return isWide
? const WideLayout()
: const NarrowLayout();
},
)
Width-based layout decisions are often preferable to relying only on orientation. Flutter's best-practice guidance also recommends avoiding unnecessary orientation locks. Flutter Adaptive Best Practices
26. Supporting Mouse and Keyboard on Desktop
Desktop applications should not depend exclusively on touch interaction. Users may expect mouse hover, keyboard navigation, shortcuts, scrolling, and pointer interactions.
Example Tooltip
Tooltip(
message: 'Open settings',
child: IconButton(
onPressed: () {},
icon: const Icon(Icons.settings),
),
)
Example Keyboard Shortcut
Shortcuts(
shortcuts: const {
SingleActivator(
LogicalKeyboardKey.control,
key: LogicalKeyboardKey.keyS,
): SaveIntent(),
},
child: Actions(
actions: {
SaveIntent: CallbackAction<SaveIntent>(
onInvoke: (intent) {
// Save data.
return null;
},
),
},
child: const Focus(
autofocus: true,
child: Text('Press Ctrl+S to save'),
),
),
)
Flutter's adaptive guidance recommends supporting different input devices, including mouse, trackpad, and keyboard interaction, particularly on larger devices. Input and Adaptive UI Best Practices
27. Platform-Specific UI Considerations
Although Flutter allows code sharing across platforms, platform conventions can still matter.
- Mobile users expect touch-friendly controls.
- Desktop users expect mouse and keyboard support.
- Desktop users commonly expect hover feedback.
- Keyboard shortcuts can improve desktop workflows.
- Context menus can be useful on desktop.
- Scrolling behavior may need to feel appropriate to the input device.
- Platform-specific navigation and interaction conventions may need consideration.
Flutter's platform-idiom guidance recommends considering the conventions users expect on each platform while still maintaining a consistent application identity. Flutter Platform Idioms
28. Adaptive Dashboard Example
class DashboardPage extends StatelessWidget {
const DashboardPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dashboard'),
),
body: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileDashboard();
}
if (constraints.maxWidth < 1000) {
return const TabletDashboard();
}
return const DesktopDashboard();
},
),
);
}
}
class MobileDashboard extends StatelessWidget {
const MobileDashboard({super.key});
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(16),
children: const [
DashboardCard(title: 'Users', value: '12,450'),
DashboardCard(title: 'Orders', value: '3,240'),
DashboardCard(title: 'Revenue', value: '₹8,45,000'),
],
);
}
}
class TabletDashboard extends StatelessWidget {
const TabletDashboard({super.key});
@override
Widget build(BuildContext context) {
return GridView.count(
padding: const EdgeInsets.all(24),
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: const [
DashboardCard(title: 'Users', value: '12,450'),
DashboardCard(title: 'Orders', value: '3,240'),
DashboardCard(title: 'Revenue', value: '₹8,45,000'),
DashboardCard(title: 'Pending', value: '128'),
],
);
}
}
class DesktopDashboard extends StatelessWidget {
const DesktopDashboard({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const SizedBox(
width: 240,
child: Center(
child: Text('Sidebar'),
),
),
Expanded(
child: GridView.count(
padding: const EdgeInsets.all(32),
crossAxisCount: 4,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
children: const [
DashboardCard(title: 'Users', value: '12,450'),
DashboardCard(title: 'Orders', value: '3,240'),
DashboardCard(title: 'Revenue', value: '₹8,45,000'),
DashboardCard(title: 'Pending', value: '128'),
],
),
),
],
);
}
}
class DashboardCard extends StatelessWidget {
final String title;
final String value;
const DashboardCard({
super.key,
required this.title,
required this.value,
});
@override
Widget build(BuildContext context) {
return Card(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(title),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
29. Creating a Reusable Responsive Builder
When many screens require the same mobile, tablet, and desktop breakpoints, a reusable widget can make the code cleaner.
class ResponsiveLayout extends StatelessWidget {
final Widget mobile;
final Widget tablet;
final Widget desktop;
const ResponsiveLayout({
super.key,
required this.mobile,
required this.tablet,
required this.desktop,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return mobile;
}
if (constraints.maxWidth < 1000) {
return tablet;
}
return desktop;
},
);
}
}
Usage
ResponsiveLayout(
mobile: const MobileHomePage(),
tablet: const TabletHomePage(),
desktop: const DesktopHomePage(),
)
30. Creating a Shared Data Model
Adaptive applications should generally share the same data and business logic while changing the presentation.
class Product {
final String name;
final double price;
final String category;
const Product({
required this.name,
required this.price,
required this.category,
});
}
const products = [
Product(
name: 'Laptop',
price: 75000,
category: 'Electronics',
),
Product(
name: 'Phone',
price: 45000,
category: 'Electronics',
),
];
The same products data can be displayed using a mobile ListView, tablet GridView, or desktop multi-column layout.
31. Mobile, Tablet and Desktop Architecture
lib/
├── main.dart
├── models/
│ └── product.dart
├── services/
│ └── product_service.dart
├── widgets/
│ ├── product_card.dart
│ ├── responsive_layout.dart
│ └── app_navigation.dart
├── screens/
│ ├── mobile/
│ │ └── mobile_home.dart
│ ├── tablet/
│ │ └── tablet_home.dart
│ └── desktop/
│ └── desktop_home.dart
└── theme/
└── app_theme.dart
This type of structure is optional. For many applications, it is preferable to keep shared widgets and data together and only separate the parts whose presentation truly differs.
32. Do Not Duplicate the Entire Application
A common beginner mistake is creating three completely independent applications:
MobileApp()
TabletApp()
DesktopApp()
This can create unnecessary duplication.
A better approach is:
Shared Data
|
+---- Shared Business Logic
|
+---- Shared Components
|
+---- Mobile Presentation
+---- Tablet Presentation
+---- Desktop Presentation
Flutter's adaptive guidance recommends abstracting shared data and breaking complex widgets into smaller reusable components. Flutter Adaptive Architecture Guidance
33. Common Mistakes
Mistake 1: Checking Only Device Type
if (isTablet) {
return TabletLayout();
}
Physical device type does not always tell you how much space your application currently has. A tablet can run an app in a narrow multi-window configuration, and a desktop browser can be resized to a small window.
Mistake 2: Using Fixed Width Everywhere
Container(
width: 1200,
child: Content(),
)
This can cause overflow on smaller windows.
Mistake 3: Making Desktop Content Full Width
Large screens do not mean every text field, paragraph, or card should stretch across the entire window.
Mistake 4: Using Too Many Breakpoints
Too many layout branches can make an application difficult to maintain. Use breakpoints where the structure actually needs to change.
Mistake 5: Ignoring Input Methods
A desktop interface should not depend exclusively on touch gestures.
Mistake 6: Ignoring Orientation and Resizing
Applications should be tested when the available window changes.
34. Best Practices
- Design for available space rather than physical device categories.
- Use
MediaQuery.sizeOf for application-window decisions.
- Use
LayoutBuilder for local widget constraints.
- Choose breakpoints based on content requirements.
- Use flexible layouts with
Expanded and Flexible.
- Use
GridView for appropriate large-screen content.
- Use
ConstrainedBox to limit excessive desktop widths.
- Use
SafeArea for content that needs protection from system UI.
- Use mobile-friendly touch targets.
- Support mouse and keyboard interaction on desktop.
- Use navigation patterns appropriate to available space.
- Keep application state when the window changes size.
- Break complex widgets into smaller reusable components.
- Test Flutter Web by resizing the browser window.
- Test portrait and landscape configurations.
- Test different text sizes and accessibility settings.
35. Testing Mobile, Tablet and Desktop UI
Mobile Testing
- Check portrait mode.
- Check landscape mode.
- Test small and large phone widths.
- Test touch interactions.
- Check scrolling.
- Check keyboard visibility with forms.
- Check safe areas and display cutouts.
Tablet Testing
- Test portrait and landscape.
- Test multi-column layouts.
- Test navigation rail.
- Test split-screen or constrained windows.
- Check content density.
Desktop Testing
- Resize the application window.
- Test mouse interactions.
- Test keyboard navigation.
- Test hover states.
- Test scrollbars.
- Test large displays.
- Check maximum content width.
36. Adaptive UI Testing with Flutter Web
Flutter Web is particularly useful for testing adaptive layouts because the browser window can be resized continuously.
flutter run -d chrome
After launching the application, resize the browser from a narrow width to a large desktop width and observe how the UI changes.
Flutter's adaptive tutorial specifically demonstrates resizing a browser window to observe adaptive layout changes. Flutter Adaptive Layout Tutorial
37. Practical Project: Mobile, Tablet and Desktop Dashboard
Build a dashboard containing:
- Dashboard statistics.
- User list.
- Product cards.
- Orders table.
- Search field.
- Filters.
- Navigation.
- Profile section.
Mobile
AppBar
|
Stats Cards
|
Search
|
Product List
|
Recent Orders
|
NavigationBar
Tablet
NavigationRail | Dashboard
|
+-- Stats Cards
+-- Product Grid
+-- Orders
Desktop
Sidebar | Dashboard Header
|
+-- Statistics
|
+-- Charts | Recent Orders
|
+-- Product Grid
|
+-- Detailed Table
38. Complete Adaptive Application Structure
class MyAdaptiveApp extends StatelessWidget {
const MyAdaptiveApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Adaptive Flutter App',
home: const AdaptiveHome(),
);
}
}
class AdaptiveHome extends StatelessWidget {
const AdaptiveHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
if (width < 600) {
return const MobileHome();
}
if (width < 1000) {
return const TabletHome();
}
return const DesktopHome();
},
),
),
);
}
}
class MobileHome extends StatelessWidget {
const MobileHome({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Mobile UI'),
);
}
}
class TabletHome extends StatelessWidget {
const TabletHome({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Tablet UI'),
);
}
}
class DesktopHome extends StatelessWidget {
const DesktopHome({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Desktop UI'),
);
}
}
39. Quick Revision Table
| Concept | Purpose |
|---|
| Mobile UI | Optimized for limited space and touch interaction. |
| Tablet UI | Uses additional space for multi-column and split layouts. |
| Desktop UI | Uses larger windows and pointer/keyboard interactions. |
| Responsive Design | Fits UI into available space. |
| Adaptive Design | Changes UI structure to remain usable in available space. |
| MediaQuery | Measures application-window information. |
| LayoutBuilder | Provides parent constraints for local layout decisions. |
| NavigationBar | Useful for navigation in smaller layouts. |
| NavigationRail | Useful for wider layouts. |
| GridView | Displays content in multiple columns. |
| Expanded | Uses available remaining space. |
| Flexible | Allows a child to flex within available space. |
| ConstrainedBox | Limits maximum or minimum dimensions. |
| SafeArea | Protects content from system UI and display cutouts. |
40. Practice Exercises
- Create a Flutter application with different mobile, tablet, and desktop layouts.
- Create a navigation system that uses
NavigationBar on mobile and NavigationRail on larger layouts.
- Create a product list that changes into a grid on larger screens.
- Create a responsive login form.
- Create a dashboard with one-column mobile cards, two-column tablet cards, and four-column desktop cards.
- Create a list-detail interface that displays both panels on desktop.
- Create a sidebar that appears only on larger screens.
- Add mouse tooltips and keyboard shortcuts for desktop users.
- Run the project in Chrome and resize the browser to test adaptive behavior.
- Test the application in portrait and landscape modes.
41. Key Takeaways
- Flutter supports building applications for mobile, tablet, desktop, and web from a shared codebase.
- Mobile layouts generally prioritize vertical content and touch-friendly interaction.
- Tablet layouts can use additional horizontal space for split views and multi-column content.
- Desktop layouts can use sidebars, navigation rails, tables, toolbars, and multi-panel interfaces.
- Responsive design helps UI fit into available space.
- Adaptive design changes the UI structure when necessary to keep it usable.
MediaQuery.sizeOf is useful for application-window-level decisions.
LayoutBuilder is useful for widget-level layout decisions.
- Breakpoints should be based on the UI's space requirements rather than only on device names.
- Large screens should not simply receive a stretched mobile interface.
- Desktop interfaces should support mouse, keyboard, trackpad, and appropriate pointer interactions.
- Reusable widgets and shared data reduce code duplication.
- Testing different window sizes is essential for adaptive Flutter applications.
42. Official Flutter Resources
43. Learn Flutter with JustAcademy
For structured Flutter training and practical application development, visit the JustAcademy Flutter Training Course.
You can also register for a Flutter course demo through the JustAcademy Course Demo Registration page.
Conclusion
Building Mobile, Tablet and Desktop UI in Flutter requires more than simply increasing or decreasing widget sizes. A professional Flutter application should adapt its navigation, content arrangement, grids, forms, dashboards, spacing, and interaction patterns according to the available space and platform context. Use MediaQuery and LayoutBuilder to measure space, establish meaningful breakpoints, and combine flexible widgets such as Expanded, Flexible, GridView, Wrap, ConstrainedBox, and SafeArea. By sharing data and business logic while adapting presentation where appropriate, you can build one maintainable Flutter application that provides a usable experience across mobile, tablet, and desktop environments.